Make LazyVStack and LazyHStack actually lazy - #52
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LazyVStack/LazyHStackweretypealiases toVStack/HStack— they had the SwiftUI name but built every element eagerly. They're now real lazy containers.Where the laziness comes from
Listtakes its data directly, so it can register an item provider. A lazy stack takes a ViewBuilder, so the data is one level down — inside theForEach. SincebuildBlockreturns a single child unwrapped,LazyVStack { ForEach(…) }hands the stack theForEachitself; a new internal_LazyElementProviderconformance exposes its count, per-element identity keys, and on-demand resolution. Elements are keyed by identity exactly as the eager path was, so element@Statestill survives scrolling and reordering.Content that isn't a single
ForEach(mixed or static) falls back to resolving children eagerly — correct, just not lazy — rather than failing.The nesting hazard
ScrollView { LazyVStack { … } }is the idiomatic SwiftUI shape, but it maps to aLazyColumninside averticalScrollColumn, which measures the child with unbounded height and crashes Compose. So when aScrollView's only child is a lazy stack, the ScrollView now yields scrolling to it.Verification
swift test— 4 new tests, 79 total passing. The load-bearing one builds a 10,000-elementLazyVStackand asserts a probe counter is0after evaluation, then exactly1after requesting index 42 — i.e. nothing is built until asked for. Others cover identity keys, the eager fallback, andLazyHStack.LazyHStackand a 10,000-rowLazyVStackon one screen, each scrolling independently — reached item 40+ and Row 2964 respectively, with no crash from the ScrollView nesting.Note
LazyVGrid/LazyHGridalready had their own lazy path and are unchanged.